package com.gitee.wsl.text.dfa

import com.gitee.wsl.ext.base.COMMA
import com.gitee.wsl.ext.string.charAt
import com.gitee.wsl.func.filter.Filter

/**
 * 敏感词工具类
 *
 * @author Looly
 */
object SensitiveUtil {
    val DEFAULT_SEPARATOR: Char = Char.COMMA
    private val sensitiveTree: WordTree = WordTree()

    val isInited: Boolean
        /**
         * @return 是否已经被初始化
         */
        get() = !sensitiveTree.isEmpty()

    /**
     * 初始化敏感词树
     *
     * @param isAsync        是否异步初始化
     * @param sensitiveWords 敏感词列表
     */
    fun init(sensitiveWords: Collection<String>, isAsync: Boolean) {
//        if (isAsync) {
//            ThreadUtil.execAsync({
//                init(sensitiveWords)
//                true
//            })
//        } else {
            init(sensitiveWords)
        //}
    }

    /**
     * 初始化敏感词树
     *
     * @param sensitiveWords 敏感词列表
     */
    fun init(sensitiveWords: Collection<String>) {
        sensitiveTree.clear()
        sensitiveTree.addWords(sensitiveWords)
        //		log.debug("Sensitive init finished, sensitives: {}", sensitiveWords);
    }

    /**
     * 初始化敏感词树
     *
     * @param sensitiveWords 敏感词列表组成的字符串
     * @param isAsync        是否异步初始化
     * @param separator      分隔符
     */
    fun init(sensitiveWords: String, separator: Char, isAsync: Boolean) {
        if (sensitiveWords.isNotBlank()) {
            init(sensitiveWords.split( separator), isAsync)
        }
    }

    /**
     * 初始化敏感词树，使用逗号分隔每个单词
     *
     * @param sensitiveWords 敏感词列表组成的字符串
     * @param isAsync        是否异步初始化
     */
    fun init(sensitiveWords: String, isAsync: Boolean) {
        init(sensitiveWords, DEFAULT_SEPARATOR, isAsync)
    }

    /**
     * 设置字符过滤规则，通过定义字符串过滤规则，过滤不需要的字符<br></br>
     * 当accept为false时，此字符不参与匹配
     *
     * @param charFilter 过滤函数
     * @since 5.4.4
     */
    fun setCharFilter(charFilter: Filter<Char>) {
        sensitiveTree.setCharFilter(charFilter)
    }

    /**
     * 是否包含敏感词
     *
     * @param text 文本
     * @return 是否包含
     */
    fun containsSensitive(text: String): Boolean {
        return sensitiveTree.isMatch(text)
    }

    /**
     * 是否包含敏感词
     *
     * @param obj bean，会被转为JSON字符串
     * @return 是否包含
     */
//    fun containsSensitive(obj: Object?): Boolean {
//        return sensitiveTree.isMatch(JSONUtil.toJsonStr(obj))
//    }

    /**
     * 查找敏感词，返回找到的第一个敏感词
     *
     * @param text 文本
     * @return 敏感词
     * @since 5.5.3
     */
    fun getFoundFirstSensitive(text: String): FoundWord? {
        return sensitiveTree.matchWord(text)
    }

    /**
     * 查找敏感词，返回找到的第一个敏感词
     *
     * @param obj bean，会被转为JSON字符串
     * @return 敏感词
     */
//    fun getFoundFirstSensitive(obj: Object?): FoundWord? {
//        return sensitiveTree.matchWord(JSONUtil.toJsonStr(obj))
//    }

    /**
     * 查找敏感词，返回找到的所有敏感词
     *
     * @param text 文本
     * @return 敏感词
     * @since 5.5.3
     */
    fun getFoundAllSensitive(text: String): List<FoundWord>? {
        return sensitiveTree.matchAllWords(text)
    }

    /**
     * 查找敏感词，返回找到的所有敏感词<br></br>
     * 密集匹配原则：假如关键词有 ab,b，文本是abab，将匹配 [ab,b,ab]<br></br>
     * 贪婪匹配（最长匹配）原则：假如关键字a,ab，最长匹配将匹配[a, ab]
     *
     * @param text           文本
     * @param isDensityMatch 是否使用密集匹配原则
     * @param isGreedMatch   是否使用贪婪匹配（最长匹配）原则
     * @return 敏感词
     */
    fun getFoundAllSensitive(text: String, isDensityMatch: Boolean, isGreedMatch: Boolean): List<FoundWord?> {
        return sensitiveTree.matchAllWords(text, -1, isDensityMatch, isGreedMatch)
    }

    /**
     * 查找敏感词，返回找到的所有敏感词
     *
     * @param bean 对象，会被转为JSON
     * @return 敏感词
     * @since 5.5.3
     */
//    fun getFoundAllSensitive(bean: Object?): List<FoundWord?>? {
//        return sensitiveTree.matchAllWords(JSONUtil.toJsonStr(bean))
//    }

    /**
     * 查找敏感词，返回找到的所有敏感词<br></br>
     * 密集匹配原则：假如关键词有 ab,b，文本是abab，将匹配 [ab,b,ab]<br></br>
     * 贪婪匹配（最长匹配）原则：假如关键字a,ab，最长匹配将匹配[a, ab]
     *
     * @param bean           对象，会被转为JSON
     * @param isDensityMatch 是否使用密集匹配原则
     * @param isGreedMatch   是否使用贪婪匹配（最长匹配）原则
     * @return 敏感词
     * @since 5.5.3
     */
//    fun getFoundAllSensitive(bean: Object?, isDensityMatch: Boolean, isGreedMatch: Boolean): List<FoundWord?>? {
//        return getFoundAllSensitive(JSONUtil.toJsonStr(bean), isDensityMatch, isGreedMatch)
//    }

    /**
     * 敏感词过滤
     *
     * @param bean               对象，会被转为JSON
     * @param isGreedMatch       贪婪匹配（最长匹配）原则：假如关键字a,ab，最长匹配将匹配[a, ab]
     * @param sensitiveProcessor 敏感词处理器，默认按匹配内容的字符数替换成*
     * @param <T>                bean的class类型
     * @return 敏感词过滤处理后的bean对象
    </T> */
//    fun <T> sensitiveFilter(bean: T?, isGreedMatch: Boolean, sensitiveProcessor: SensitiveProcessor?): T? {
//        val jsonText: String = JSONUtil.toJsonStr(bean)
//        @SuppressWarnings("unchecked") val c: Class<T?>? = bean.getClass() as Class<T?>?
//        return JSONUtil.toBean(
//            SensitiveUtil.sensitiveFilter(jsonText, isGreedMatch, sensitiveProcessor),
//            c
//        )
//    }

    /**
     * 处理过滤文本中的敏感词，默认替换成*
     *
     * @param text 文本
     * @return 敏感词过滤处理后的文本
     * @since 5.7.21
     */
    fun sensitiveFilter(text: String): String {
        return sensitiveFilter(text, true)
    }

    /**
     * 处理过滤文本中的敏感词，默认替换成*
     *
     * @param text               文本
     * @param isGreedMatch       贪婪匹配（最长匹配）原则：假如关键字a,ab，最长匹配将匹配[a, ab]
     * @param sensitiveProcessor 敏感词处理器，默认按匹配内容的字符数替换成*
     * @return 敏感词过滤处理后的文本
     */
    fun sensitiveFilter(text: String, isGreedMatch: Boolean, sensitiveProcessor: SensitiveProcessor = object:SensitiveProcessor{}): String {
        //var sensitiveProcessor = sensitiveProcessor
        if (text.isEmpty()) {
            return text
        }

        //敏感词过滤场景下，不需要密集匹配
        val foundWordList= SensitiveUtil.getFoundAllSensitive(text, true, isGreedMatch)
        if (foundWordList.isEmpty()) {
            return text
        }
        //sensitiveProcessor = if (sensitiveProcessor == null) object : SensitiveProcessor {} else sensitiveProcessor

        val foundWordMap = HashMap<Int, FoundWord>(foundWordList.size, 1f)
        foundWordList.forEach{ foundWord -> foundWordMap.put(foundWord!!.startIndex, foundWord) }
        val length: Int = text.length
        val textStringBuilder = StringBuilder()
        var i = 0
        while (i < length) {
            val fw: FoundWord? = foundWordMap[i]
            if (fw != null) {
                textStringBuilder.append(sensitiveProcessor.process(fw))
                i = fw.endIndex
            } else {
                textStringBuilder.append(text.charAt(i))
            }
            i++
        }
        return textStringBuilder.toString()
    }
}
